package com.legendframework.core.aop;

import com.legendframework.core.dao.support.ClassUtils;
import sun.misc.ProxyGenerator;
import sun.reflect.Reflection;

import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

public final class LegendCglibProxy {

    /**
     * 代理缓存池
     */
    private static Object weakCache;

    private static Method weakCacheGetMethod;

    private static Method weakCacheContainsValue;

    static {
        try {
            Class<?> weakCacheClass = Class.forName("java.lang.reflect.WeakCache");
            for (Method method : weakCacheClass.getDeclaredMethods()) {
                if ("get".equals(method.getName())) {
                    weakCacheGetMethod = method;
                    weakCacheGetMethod.setAccessible(true);
                }
                if ("containsValue".equals(method.getName())) {
                    weakCacheContainsValue = method;
                    weakCacheContainsValue.setAccessible(true);
                }
            }
            Constructor<?> constructor = weakCacheClass.getDeclaredConstructor(BiFunction.class, BiFunction.class);
            constructor.setAccessible(true);
            weakCache = constructor.newInstance(
                    (BiFunction<ClassLoader, Class<?>[], Object>) (classLoader, classes) -> new KeyX(classes),
                    new ProxyClassFactory());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断这个字节码是否为代理类
     * @param cl
     * @return
     */
    public static boolean isProxyClass(Class<?> cl) {
        try {
            return LegendProxy.class.isAssignableFrom(cl) && (boolean) weakCacheContainsValue.invoke(weakCache,cl);
        } catch (Throwable e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 类似cglib的动态代理，基于类的代理
     *
     * 被代理的类必须是public或者protected且不是抽象类与终类
     *
     * @param loader 类加载器
     * @param cls 需要代理的字节码
     * @param h 处理器
     * @param <T> 类的类型
     * @return
     * @throws IllegalArgumentException
     */
    public static <T> T newProxyInstance(ClassLoader loader, Class<T> cls,Class<?>[] interfaces, InvocationHandler h) {
        if ((!Modifier.isPublic(cls.getModifiers()) && !Modifier.isProtected(cls.getModifiers())) || Modifier.isFinal(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers())) {
            throw new IllegalArgumentException("被代理的类必须是public或者protected且不是抽象类与终类");
        }
        if (cls.isInterface() || cls.isEnum() || cls.isAnnotation()) {
            throw new IllegalArgumentException("被代理的类必须是class类型");
        }

        try {

            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                Method checkProxyAccess = Proxy.class.getDeclaredMethod("checkProxyAccess", Class.class, ClassLoader.class, Class[].class);
                checkProxyAccess.setAccessible(true);
                checkProxyAccess.invoke(null,Reflection.getCallerClass(), loader, new Class<?>[]{cls});
            }

            List<Class> list = new ArrayList<>(Collections.singletonList(cls));
            if (interfaces != null) {
                list.addAll(Arrays.stream(interfaces).filter(item -> !item.isAssignableFrom(cls)).collect(Collectors.toList()));
                if (!list.contains(LegendProxy.class) && list.stream().noneMatch(LegendProxy.class::isAssignableFrom)) {
                    list.add(LegendProxy.class);
                }
            } else if (!LegendProxy.class.isAssignableFrom(cls)){
                list.add(LegendProxy.class);
            }
            Class<?> proxyClass = (Class<?>) weakCacheGetMethod.invoke(weakCache, loader, list.toArray(new Class[0]));

            if (sm != null) {
                Method checkNewProxyPermission = Proxy.class.getDeclaredMethod("checkNewProxyPermission", Class.class, Class.class);
                checkNewProxyPermission.setAccessible(true);
                checkNewProxyPermission.invoke(null,Reflection.getCallerClass(), proxyClass);
            }

            /*
             * Invoke its constructor with the designated invocation handler.
             */
            final Constructor<?> cons = proxyClass.getConstructor(InvocationHandler.class);
            if (!Modifier.isPublic(cons.getModifiers())) {
                AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
                    cons.setAccessible(true);
                    return null;
                });
            }
            return (T) cons.newInstance(h);
        } catch (Throwable e) {
            throw new IllegalArgumentException("代理类：["+cls.getName()+"] 失败 ,原因：",e);
        }
    }

    static int i = 0;

    /**
     * 代理工厂
     */
    private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$LegendCglibProxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = "com.sun.proxy.";     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            ArrayList<Class<?>> classes = new ArrayList<>(Arrays.asList(interfaces));
            try {
                byte[] proxyClassFile = CglibProxyGenerator.generateProxyClass(
                        proxyName, classes.remove(0),classes.toArray(new Class[0]), accessFlags);

                Method defineClass0 = Proxy.class.getDeclaredMethod("defineClass0", ClassLoader.class, String.class, byte[].class, int.class, int.class);
                defineClass0.setAccessible(true);
                return (Class<?>) defineClass0.invoke(null,loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
            } catch (Throwable e){
                throw new IllegalArgumentException(e);
            }
        }
    }

    /*
     * a key used for proxy class with any number of implemented interfaces
     * (used here for 3 or more only)
     */
    private static final class KeyX {
        private final int hash;
        private final WeakReference<Class<?>>[] refs;

        @SuppressWarnings("unchecked")
        KeyX(Class<?>[] interfaces) {
            hash = Arrays.hashCode(interfaces);
            refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                refs[i] = new WeakReference<>(interfaces[i]);
            }
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj ||
                    obj != null &&
                            obj.getClass() == KeyX.class &&
                            equals(refs, ((KeyX) obj).refs);
        }

        private static boolean equals(WeakReference<Class<?>>[] refs1,
                                      WeakReference<Class<?>>[] refs2) {
            if (refs1.length != refs2.length) {
                return false;
            }
            for (int i = 0; i < refs1.length; i++) {
                Class<?> intf = refs1[i].get();
                if (intf == null || intf != refs2[i].get()) {
                    return false;
                }
            }
            return true;
        }
    }

    /**
     * 字节码构建类
     */
    private final static class CglibProxyGenerator {

        private static Constructor<ProxyGenerator> constructor;
        private static Constructor fieldInfoConstructor;
        private static Constructor methodInfoConstructor;
        private static Constructor exceptionTableEntryConstructor;

        private static Field saveGeneratedFiles;
        private static Field hashCodeMethod;
        private static Field equalsMethod;
        private static Field toStringMethod;
        private static Field interfaces;
        private static Field proxyMethods;
        private static Field methods;
        private static Field fields;
        private static Field cp;
        private static Field className;
        private static Field accessFlags;

        private static Field proxyMethodMethodFieldName;
        private static Field proxyMethodParameterTypes;
        private static Field proxyMethodReturnType;
        private static Field proxyMethodMethodName;
        private static Field proxyMethodExceptionTypes;

        private static Field methodInfoCode;
        private static Field methodInfoMaxStack;
        private static Field methodInfoMaxLocals;
        private static Field methodInfoDeclaredExceptions;
        private static Field methodInfoExceptionTable;

        private static Method addProxyMethod;
        private static Method checkReturnTypes;
        private static Method generateConstructor;
        private static Method generateStaticInitializer;
        private static Method dotToSlash;
        private static Method code_aload;
        private static Method getMethodDescriptor;
        private static Method getWordsPerType;
        private static Method code_ipush;
        private static Method computeUniqueCatchList;
        private static Method code_astore;

        private static Method cpGetClass;
        private static Method cpSetReadOnly;
        private static Method cpWrite;
        private static Method cpGetMethodRef;
        private static Method cpGetFieldRef;
        private static Method cpGetInterfaceMethodRef;

        private static Method fieldInfoWrite;

        private static Method methodInfoWrite;

        private static Method proxyMethodGenerateMethod;
        private static Method proxyMethodCodeWrapArgument;
        private static Method proxyMethodCodeUnwrapReturnValue;


        static {
            try {
                Class.forName(Proxy.class.getName());
                //构造器
                constructor = ProxyGenerator.class.getDeclaredConstructor(String.class, Class[].class, int.class);
                //字段构造
                saveGeneratedFiles = ProxyGenerator.class.getDeclaredField("saveGeneratedFiles");
                hashCodeMethod = ProxyGenerator.class.getDeclaredField("hashCodeMethod");
                equalsMethod = ProxyGenerator.class.getDeclaredField("equalsMethod");
                toStringMethod = ProxyGenerator.class.getDeclaredField("toStringMethod");
                interfaces = ProxyGenerator.class.getDeclaredField("interfaces");
                proxyMethods = ProxyGenerator.class.getDeclaredField("proxyMethods");
                methods = ProxyGenerator.class.getDeclaredField("methods");
                fields = ProxyGenerator.class.getDeclaredField("fields");
                cp = ProxyGenerator.class.getDeclaredField("cp");
                className = ProxyGenerator.class.getDeclaredField("className");
                accessFlags = ProxyGenerator.class.getDeclaredField("accessFlags");

                Class<?> proxyMethodClass = Class.forName("sun.misc.ProxyGenerator$ProxyMethod");
                proxyMethodMethodFieldName = proxyMethodClass.getDeclaredField("methodFieldName");
                proxyMethodParameterTypes = proxyMethodClass.getDeclaredField("parameterTypes");
                proxyMethodReturnType = proxyMethodClass.getDeclaredField("returnType");
                proxyMethodMethodName = proxyMethodClass.getDeclaredField("methodName");
                proxyMethodExceptionTypes = proxyMethodClass.getDeclaredField("exceptionTypes");


                //方法构造
                addProxyMethod = ProxyGenerator.class.getDeclaredMethod("addProxyMethod", Method.class, Class.class);
                addProxyMethod.setAccessible(true);
                checkReturnTypes = ProxyGenerator.class.getDeclaredMethod("checkReturnTypes", List.class);
                checkReturnTypes.setAccessible(true);
                generateConstructor = ProxyGenerator.class.getDeclaredMethod("generateConstructor");
                generateConstructor.setAccessible(true);
                dotToSlash = ProxyGenerator.class.getDeclaredMethod("dotToSlash", String.class);
                dotToSlash.setAccessible(true);
                generateStaticInitializer = ProxyGenerator.class.getDeclaredMethod("generateStaticInitializer");
                generateStaticInitializer.setAccessible(true);
                code_aload = ProxyGenerator.class.getDeclaredMethod("code_aload",int.class,DataOutputStream.class);
                code_aload.setAccessible(true);
                getMethodDescriptor = ProxyGenerator.class.getDeclaredMethod("getMethodDescriptor",Class[].class,Class.class);
                getMethodDescriptor.setAccessible(true);
                getWordsPerType = ProxyGenerator.class.getDeclaredMethod("getWordsPerType",Class.class);
                getWordsPerType.setAccessible(true);
                code_ipush = ProxyGenerator.class.getDeclaredMethod("code_ipush",int.class,DataOutputStream.class);
                code_ipush.setAccessible(true);
                computeUniqueCatchList = ProxyGenerator.class.getDeclaredMethod("computeUniqueCatchList",Class[].class);
                computeUniqueCatchList.setAccessible(true);
                code_astore = ProxyGenerator.class.getDeclaredMethod("code_astore",int.class,DataOutputStream.class);
                code_astore.setAccessible(true);




                Class<?> cpClass = Class.forName("sun.misc.ProxyGenerator$ConstantPool");
                cpGetClass = cpClass.getDeclaredMethod("getClass", String.class);
                cpGetClass.setAccessible(true);
                cpSetReadOnly = cpClass.getDeclaredMethod("setReadOnly");
                cpSetReadOnly.setAccessible(true);
                cpWrite = cpClass.getDeclaredMethod("write", OutputStream.class);
                cpWrite.setAccessible(true);
                cpGetMethodRef = cpClass.getDeclaredMethod("getMethodRef", String.class,String.class,String.class);
                cpGetMethodRef.setAccessible(true);
                cpGetFieldRef = cpClass.getDeclaredMethod("getFieldRef", String.class,String.class,String.class);
                cpGetFieldRef.setAccessible(true);
                cpGetInterfaceMethodRef = cpClass.getDeclaredMethod("getInterfaceMethodRef", String.class,String.class,String.class);
                cpGetInterfaceMethodRef.setAccessible(true);



                Class<?> fieldInfoClass = Class.forName("sun.misc.ProxyGenerator$FieldInfo");
                fieldInfoWrite = fieldInfoClass.getDeclaredMethod("write", DataOutputStream.class);
                fieldInfoWrite.setAccessible(true);
                fieldInfoConstructor = fieldInfoClass.getDeclaredConstructor(ProxyGenerator.class, String.class, String.class, int.class);

                Class<?> methodInfoClass = Class.forName("sun.misc.ProxyGenerator$MethodInfo");
                methodInfoWrite = methodInfoClass.getDeclaredMethod("write", DataOutputStream.class);
                methodInfoWrite.setAccessible(true);
                methodInfoConstructor = methodInfoClass.getDeclaredConstructor(ProxyGenerator.class, String.class, String.class, int.class);
                methodInfoCode = methodInfoClass.getDeclaredField("code");
                methodInfoMaxStack = methodInfoClass.getDeclaredField("maxStack");
                methodInfoMaxLocals = methodInfoClass.getDeclaredField("maxLocals");
                methodInfoDeclaredExceptions = methodInfoClass.getDeclaredField("declaredExceptions");
                methodInfoExceptionTable = methodInfoClass.getDeclaredField("exceptionTable");


                proxyMethodGenerateMethod = proxyMethodClass.getDeclaredMethod("generateMethod");
                proxyMethodGenerateMethod.setAccessible(true);
                proxyMethodCodeWrapArgument = proxyMethodClass.getDeclaredMethod("codeWrapArgument",Class.class,int.class,DataOutputStream.class);
                proxyMethodCodeWrapArgument.setAccessible(true);
                proxyMethodCodeUnwrapReturnValue = proxyMethodClass.getDeclaredMethod("codeUnwrapReturnValue",Class.class,DataOutputStream.class);
                proxyMethodCodeUnwrapReturnValue.setAccessible(true);

                Class<?> exceptionTableEntryClass = Class.forName("sun.misc.ProxyGenerator$ExceptionTableEntry");
                exceptionTableEntryConstructor = exceptionTableEntryClass.getDeclaredConstructor(short.class, short.class, short.class, short.class);

            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        private static ProxyGenerator getConstructor(String var0, Class<?>[] var1, int var2) throws IllegalAccessException, InvocationTargetException, InstantiationException {
            constructor.setAccessible(true);
            return constructor.newInstance(var0, var1, var2);
        }

        private static Object getFieldInfoConstructor(ProxyGenerator proxyGenerator, String var0, String var1, int var2) throws IllegalAccessException, InvocationTargetException, InstantiationException {
            fieldInfoConstructor.setAccessible(true);
            return fieldInfoConstructor.newInstance(proxyGenerator, var0, var1, var2);
        }

        private static Object getMethodInfoConstructor(ProxyGenerator proxyGenerator, String var0, String var1, int var2) throws IllegalAccessException, InvocationTargetException, InstantiationException {
            methodInfoConstructor.setAccessible(true);
            return methodInfoConstructor.newInstance(proxyGenerator, var0, var1, var2);
        }

        private static Object getExceptionTableEntryConstructor(short var0, short var1, short var2 , short var3) throws IllegalAccessException, InvocationTargetException, InstantiationException {
            exceptionTableEntryConstructor.setAccessible(true);
            return exceptionTableEntryConstructor.newInstance(var0, var1, var2 , var3);
        }

        private static boolean getSaveGeneratedFiles(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            saveGeneratedFiles.setAccessible(true);
            return (boolean) saveGeneratedFiles.get(proxyGenerator);
        }

        private static Method getHashCodeMethod(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            hashCodeMethod.setAccessible(true);
            return (Method) hashCodeMethod.get(proxyGenerator);
        }

        private static Method getEqualsMethod(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            equalsMethod.setAccessible(true);
            return (Method) equalsMethod.get(proxyGenerator);
        }

        private static Method getToStringMethod(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            toStringMethod.setAccessible(true);
            return (Method) toStringMethod.get(proxyGenerator);
        }

        private static Class<?>[] getInterfaces(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            interfaces.setAccessible(true);
            return (Class<?>[]) interfaces.get(proxyGenerator);
        }

        private static Map<String, List> getProxyMethods(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            proxyMethods.setAccessible(true);
            return (Map<String, List>) proxyMethods.get(proxyGenerator);
        }

        private static List getMethods(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            methods.setAccessible(true);
            return (List) methods.get(proxyGenerator);
        }

        private static List getFields(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            fields.setAccessible(true);
            return (List) fields.get(proxyGenerator);
        }

        private static Object getCp(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            cp.setAccessible(true);
            return cp.get(proxyGenerator);
        }

        private static Object getClassName(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            className.setAccessible(true);
            return className.get(proxyGenerator);
        }

        private static int getAccessFlags(ProxyGenerator proxyGenerator) throws IllegalAccessException {
            accessFlags.setAccessible(true);
            return (int) accessFlags.get(proxyGenerator);
        }

        private static String getProxyMethodMethodFieldName(Object obj) throws IllegalAccessException {
            proxyMethodMethodFieldName.setAccessible(true);
            return (String) proxyMethodMethodFieldName.get(obj);
        }

        private static ByteArrayOutputStream getMethodInfoCode(Object obj) throws IllegalAccessException {
            methodInfoCode.setAccessible(true);
            return (ByteArrayOutputStream) methodInfoCode.get(obj);
        }

        private static short getMethodInfoMaxStack(Object obj) throws IllegalAccessException {
            methodInfoMaxStack.setAccessible(true);
            return (short) methodInfoMaxStack.get(obj);
        }

        private static short getMethodInfoMaxLocals(Object obj) throws IllegalAccessException {
            methodInfoMaxLocals.setAccessible(true);
            return (short) methodInfoMaxLocals.get(obj);
        }

        private static short[] getMethodInfoDeclaredExceptions(Object obj) throws IllegalAccessException {
            methodInfoDeclaredExceptions.setAccessible(true);
            return (short[]) methodInfoDeclaredExceptions.get(obj);
        }

        private static Class<?>[] getProxyMethodParameterTypes(Object obj) throws IllegalAccessException {
            proxyMethodParameterTypes.setAccessible(true);
            return (Class<?>[]) proxyMethodParameterTypes.get(obj);
        }

        private static Class<?> getProxyMethodReturnType(Object obj) throws IllegalAccessException {
            proxyMethodReturnType.setAccessible(true);
            return (Class<?>) proxyMethodReturnType.get(obj);
        }

        private static String getProxyMethodMethodName(Object obj) throws IllegalAccessException {
            proxyMethodMethodName.setAccessible(true);
            return (String) proxyMethodMethodName.get(obj);
        }

        private static Class<?>[] getProxyMethodExceptionTypes(Object obj) throws IllegalAccessException {
            proxyMethodExceptionTypes.setAccessible(true);
            return (Class<?>[]) proxyMethodExceptionTypes.get(obj);
        }

        private static List getMethodInfoExceptionTable(Object obj) throws IllegalAccessException {
            methodInfoExceptionTable.setAccessible(true);
            return (List) methodInfoExceptionTable.get(obj);
        }


        public static byte[] generateProxyClass(String var0, Class<?> var1, Class<?>[] interfaces ) throws Throwable {
            return generateProxyClass(var0, var1, interfaces,49);
        }

        public static byte[] generateProxyClass(final String var0, Class<?> var1, Class<?>[] interfaces , int var2) throws Throwable {
            //反射实例化
            ProxyGenerator proxyGenerator = getConstructor(var0, interfaces, var2);
            final byte[] var4 = generateClassFile(proxyGenerator, var1,interfaces);
            if (getSaveGeneratedFiles(proxyGenerator)) {
                AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
                    try {
                        int var11 = var0.lastIndexOf(46);
                        Path var21;
                        if (var11 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var11).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var21 = var3.resolve(var0.substring(var11 + 1, var0.length()) + ".class");
                        } else {
                            var21 = Paths.get(var0 + ".class");
                        }

                        Files.write(var21, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                });
            }

            return var4;
        }

        private static byte[] generateClassFile(ProxyGenerator proxyGenerator, Class<?> superClass , Class<?>[] interfaces) throws Throwable {
            //获取这个类的所有方法，包括父类
            List<Method> methodsByClass = ClassUtils.getMethodsByClass(superClass);
            for (Class<?> inter : interfaces) {
                Method[] methods = inter.getMethods();
                for (Method method : methods) {
                    if (methodsByClass.stream().noneMatch(item ->
                            item.getName().equals(method.getName()) &&
                                    ClassUtils.compare(item.getParameterTypes(), method.getParameterTypes()))){
                        methodsByClass.add(method);
                    }
                }
            }
            //构建方法
            if (methodsByClass.stream().noneMatch(item -> "hashCode".equals(item.getName()) && item.getParameterCount() == 0)) {
                addProxyMethod.invoke(proxyGenerator, getHashCodeMethod(proxyGenerator), Object.class);
            }
            if (methodsByClass.stream().noneMatch(item -> "equals".equals(item.getName()) && ClassUtils.compare(item.getParameterTypes(),new Class<?>[]{Object.class}))) {
                addProxyMethod.invoke(proxyGenerator, getEqualsMethod(proxyGenerator), Object.class);
            }
            if (methodsByClass.stream().noneMatch(item -> "toString".equals(item.getName()) && item.getParameterCount() == 0)) {
                addProxyMethod.invoke(proxyGenerator, getToStringMethod(proxyGenerator), Object.class);
            }

            for (Method method : methodsByClass) {
                if (Modifier.isPublic(method.getModifiers()) && !Modifier.isFinal(method.getModifiers())) {
                    addProxyMethod.invoke(proxyGenerator, method, method.getDeclaringClass());
                }
            }

            Iterator var11 = getProxyMethods(proxyGenerator).values().iterator();

            List var12;
            while (var11.hasNext()) {
                var12 = (List) var11.next();
                checkReturnTypes.invoke(proxyGenerator, var12);
            }

            List methods = getMethods(proxyGenerator);
            List fields = getFields(proxyGenerator);
            Object cp = getCp(proxyGenerator);

            Iterator var15;
            try {
                methods.add(generateConstructor(proxyGenerator,superClass));
                Map<String, List> proxyMethods = getProxyMethods(proxyGenerator);
                var11 = proxyMethods.values().iterator();

                while (var11.hasNext()) {
                    var12 = (List) var11.next();
                    var15 = var12.iterator();

                    while (var15.hasNext()) {
                        Object var16 = var15.next();
                        fields.add(getFieldInfoConstructor(proxyGenerator, getProxyMethodMethodFieldName(var16), "Ljava/lang/reflect/Method;", 10));
                        methods.add(generateMethod(proxyGenerator,var16));
                    }
                }
                fields.add(getFieldInfoConstructor(proxyGenerator, "h", "Ljava/lang/reflect/InvocationHandler;", 2));

                methods.add(generateStaticInitializer.invoke(proxyGenerator));
            } catch (Exception e) {
                throw e;
            }

            if (methods.size() > 65535) {
                throw new IllegalArgumentException("method limit exceeded");
            } else if (fields.size() > 65535) {
                throw new IllegalArgumentException("field limit exceeded");
            } else {
                cpGetClass.invoke(cp, dotToSlash.invoke(proxyGenerator, getClassName(proxyGenerator)));
                cpGetClass.invoke(cp, dotToSlash.invoke(proxyGenerator, superClass.getName()));

                Class[] var1 = getInterfaces(proxyGenerator);
                for (Class aClass : var1) {
                    cpGetClass.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,aClass.getName()));
                }

                cpSetReadOnly.invoke(cp);
                ByteArrayOutputStream var13 = new ByteArrayOutputStream();
                DataOutputStream var14 = new DataOutputStream(var13);

                try {
                    var14.writeInt(-889275714);
                    var14.writeShort(0);
                    var14.writeShort(49);
                    cpWrite.invoke(cp, var14);
                    var14.writeShort(getAccessFlags(proxyGenerator));
                    var14.writeShort((short) cpGetClass.invoke(cp, dotToSlash.invoke(proxyGenerator, getClassName(proxyGenerator))));
                    var14.writeShort((short) cpGetClass.invoke(cp, dotToSlash.invoke(proxyGenerator, superClass.getName())));

                    var14.writeShort(getInterfaces(proxyGenerator).length);
                    Class[] var17 = getInterfaces(proxyGenerator);
                    int var18 = var17.length;

                    for (int var19 = 0; var19 < var18; ++var19) {
                        Class var22 = var17[var19];
                        var14.writeShort((short) cpGetClass.invoke(cp, dotToSlash.invoke(proxyGenerator, var22.getName())));
                    }

                    var14.writeShort(fields.size());
                    var15 = fields.iterator();

                    while (var15.hasNext()) {
                        Object var20 = var15.next();
                        fieldInfoWrite.invoke(var20, var14);
                    }

                    var14.writeShort(methods.size());
                    var15 = methods.iterator();

                    while (var15.hasNext()) {
                        Object var21 = var15.next();
                        methodInfoWrite.invoke(var21, var14);
                    }

                    var14.writeShort(0);
                    return var13.toByteArray();
                } catch (IOException var9) {
                    throw new InternalError("unexpected I/O Exception", var9);
                }
            }
        }

        private static Object generateConstructor(ProxyGenerator proxyGenerator,Class<?> superClass) throws Throwable {
            Object var1 = getMethodInfoConstructor(proxyGenerator,"<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
            DataOutputStream var2 = new DataOutputStream(getMethodInfoCode(var1));
            code_aload.invoke(proxyGenerator,0,var2);
            var2.writeByte(183);
            var2.writeShort((short) cpGetMethodRef.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,superClass.getName()),"<init>","()V"));
            code_aload.invoke(proxyGenerator,0,var2);
            code_aload.invoke(proxyGenerator,1,var2);

            //赋值
            var2.writeByte(181);
            var2.writeShort((short) cpGetFieldRef.invoke(getCp(proxyGenerator),
                    dotToSlash.invoke(proxyGenerator,getClassName(proxyGenerator)),
                    "h",
                    "Ljava/lang/reflect/InvocationHandler;"));

            var2.writeByte(177);
            methodInfoMaxStack.setAccessible(true);
            methodInfoMaxStack.set(var1,(short)10);
            methodInfoMaxLocals.setAccessible(true);
            methodInfoMaxLocals.set(var1,(short)2);
            methodInfoDeclaredExceptions.setAccessible(true);
            methodInfoDeclaredExceptions.set(var1,new short[0]);
            return var1;
        }

        private static Object generateMethod(ProxyGenerator proxyGenerator , Object proxyMethod) throws Throwable {
            Class<?>[] parameterTypes = getProxyMethodParameterTypes(proxyMethod);
            Class<?>[] exceptionTypes = getProxyMethodExceptionTypes(proxyMethod);
            Class<?> returnType = getProxyMethodReturnType(proxyMethod);
            String var1 = (String) getMethodDescriptor.invoke(proxyGenerator,parameterTypes,returnType);
            Object var2 = methodInfoConstructor.newInstance(proxyGenerator,getProxyMethodMethodName(proxyMethod), var1, 17);
            int[] var3 = new int[parameterTypes.length];
            int var4 = 1;

            for(int var5 = 0; var5 < var3.length; ++var5) {
                var3[var5] = var4;
                var4 += (int) getWordsPerType.invoke(proxyGenerator,parameterTypes[var5]);
            }

            byte var7 = 0;
            DataOutputStream var9 = new DataOutputStream(getMethodInfoCode(var2));
            code_aload.invoke(proxyGenerator,0,var9);
            var9.writeByte(180);
            var9.writeShort((short) cpGetFieldRef.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,getClassName(proxyGenerator)), "h","Ljava/lang/reflect/InvocationHandler;"));
            code_aload.invoke(proxyGenerator,0,var9);
            var9.writeByte(178);
            var9.writeShort((short) cpGetFieldRef.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,getClassName(proxyGenerator)),getProxyMethodMethodFieldName(proxyMethod),"Ljava/lang/reflect/Method;"));
            if (parameterTypes.length > 0) {
                code_ipush.invoke(proxyGenerator,parameterTypes.length,var9);
                var9.writeByte(189);
                var9.writeShort((short)cpGetClass.invoke(getCp(proxyGenerator),"java/lang/Object"));
                for(int var10 = 0; var10 < parameterTypes.length; ++var10) {
                    var9.writeByte(89);
                    code_ipush.invoke(proxyGenerator,var10,var9);
                    proxyMethodCodeWrapArgument.invoke(proxyMethod,parameterTypes[var10], var3[var10], var9);
                    var9.writeByte(83);
                }
            } else {
                var9.writeByte(1);
            }

            var9.writeByte(185);

            var9.writeShort((short) cpGetInterfaceMethodRef.invoke(getCp(proxyGenerator),"java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
            var9.writeByte(4);
            var9.writeByte(0);
            if (returnType == Void.TYPE) {
                var9.writeByte(87);
                var9.writeByte(177);
            } else {
                proxyMethodCodeUnwrapReturnValue.invoke(proxyMethod,returnType,var9);
            }

            short var6;
            short var8 = var6 = (short) getMethodInfoCode(var2).size();

            List var13 = (List) computeUniqueCatchList.invoke(null,new Object[]{exceptionTypes});
            if (var13.size() > 0) {
                Iterator var11 = var13.iterator();
                List exceptionTable = getMethodInfoExceptionTable(var2);

                while(var11.hasNext()) {
                    Class var12 = (Class)var11.next();
                    exceptionTable.add(getExceptionTableEntryConstructor(var7, var8, var6,(short) cpGetClass.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,var12.getName()))));
                }

                var9.writeByte(191);
                var6 = (short)getMethodInfoCode(var2).size();

                exceptionTable.add(getExceptionTableEntryConstructor(var7, var8, var6,(short) cpGetClass.invoke(getCp(proxyGenerator),"java/lang/Throwable")));
                code_astore.invoke(proxyGenerator,var4,var9);
                var9.writeByte(187);
                var9.writeShort((short)cpGetClass.invoke(getCp(proxyGenerator),"java/lang/reflect/UndeclaredThrowableException"));
                var9.writeByte(89);
                code_aload.invoke(proxyGenerator,var4,var9);
                var9.writeByte(183);
                var9.writeShort((short)cpGetMethodRef.invoke(getCp(proxyGenerator),"java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
                var9.writeByte(191);
            }

            if (getMethodInfoCode(var2).size() > 65535) {
                throw new IllegalArgumentException("code size limit exceeded");
            } else {
                methodInfoMaxStack.setAccessible(true);
                methodInfoMaxStack.set(var2,(short) 10);
                methodInfoMaxLocals.setAccessible(true);
                methodInfoMaxLocals.set(var2,(short)(var4 + 1));
                methodInfoDeclaredExceptions.setAccessible(true);
                methodInfoDeclaredExceptions.set(var2,new short[exceptionTypes.length]);

                short[] declaredExceptions = getMethodInfoDeclaredExceptions(var2);
                for(int var14 = 0; var14 < exceptionTypes.length; ++var14) {
                    declaredExceptions[var14] = (short) cpGetClass.invoke(getCp(proxyGenerator),dotToSlash.invoke(proxyGenerator,exceptionTypes[var14].getName()));
                }

                return var2;
            }
        }
    }


}
